﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
using System.Text;
using Caliburn.Micro;
using Zcu.PanelComposer.Interfaces;
using System.Diagnostics;
using Zcu.PanelComposer.Views.Logging;
using System.Windows;

namespace Zcu.PanelComposer.Views
{
    /// <summary>
    /// Zavadec aplikace.
    /// Zavadec vytvari kontajner vsech instanci, ktere jsou prirazeny jednotlivym interfacum.
    /// Tento kontajner pak umoznuje snadnou distribuci instanci po aplikaci.
    /// Tridy s atributy Export jsou automaticky pridavany do kontajneru.
    /// K jejich vytazeni na konretni strance (instanci) postaci k vlastnosti pridat atribut Import
    /// popr do konstruktoru (Dependency Injection vs Constructor Injection).
    /// 
    /// Zavadec je silne typovy, tento typ bude vyuzit pri nastartovani aplikace jako vychozi okno/obrazovka.
    /// </summary>
    public class AppBootstrapper : Bootstrapper<IShell>
    {
        #region Fields

        /// <summary>
        /// Reference na kontajner.
        /// </summary>
        private CompositionContainer _container;

        /// <summary>
        /// Reference na katalog.
        /// </summary>
        private DirectoryCatalog _dirCatalog;

        /// <summary>
        /// Reference na logger.
        /// </summary>
        private readonly ILog _log = LogManager.GetLog(typeof(AppBootstrapper));

        #endregion
   
        #region Constructors

        /// <summary>
        /// Initializes the <see cref="AppBootstrapper"/> class.
        /// </summary>
        static AppBootstrapper()
        {
            LogManager.GetLog = type => new Log4netLogger(type);
        }

        public AppBootstrapper()
        { }

        #endregion

        #region Configure section

        /// <summary>
        /// Override to configure the framework and setup your IoC container.
        /// </summary>
        protected override void Configure()
        {
            _dirCatalog = new DirectoryCatalog(".", "*.*");
            _container = new CompositionContainer(_dirCatalog);

            var batch = new CompositionBatch();
            batch.AddExportedValue<IWindowManager>(new WindowManager());
            batch.AddExportedValue<IEventAggregator>(new EventAggregator());
            batch.AddExportedValue(_container);
            _container.Compose(batch);
        }

        /// <summary>
        /// Override to tell the framework where to find assemblies to inspect for views, etc.
        /// </summary>
        /// <returns>
        /// A list of assemblies to inspect.
        /// </returns>
        protected override IEnumerable<Assembly> SelectAssemblies()
        {
            List<Assembly> assemblies = new List<Assembly>();
            assemblies.Add(Assembly.GetExecutingAssembly());
            //foreach (var item in _dirCatalog.LoadedFiles)
            //{
            //    assemblies.Add(Assembly.LoadFile(item));
            //}
            /// TODO zautomatizovat
            assemblies.Add(Assembly.LoadFrom("PanelComposer.ViewModels.dll"));
            assemblies.Add(Assembly.LoadFrom("PanelComposer.Models.dll"));
            return assemblies;
        }

        #endregion

        #region BuildUp section

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        protected override object GetInstance(Type serviceType, string key)
        {
            string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
            var exports = _container.GetExportedValues<object>(contract);

            if (exports.Count() > 0)
                return exports.First();

            throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
        }

        /// <summary>
        /// Gets all instances.
        /// </summary>
        /// <param name="serviceType">Type of the service.</param>
        /// <returns></returns>
        protected override IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        }

        /// <summary>
        /// Override this to provide an IoC specific implementation.
        /// </summary>
        /// <param name="instance">The instance to perform injection on.</param>
        protected override void BuildUp(object instance)
        {
            _container.SatisfyImportsOnce(instance);
        }

        #endregion

        #region Error handling

        /// <summary>
        /// Zachytavani vyjimek na urovni aplikace.
        /// Pokud kdekoliv propadne vyjimka, tak zde se zachyti.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void OnUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            base.OnUnhandledException(sender, e);
            _log.Error(e.Exception);

            if (e.Exception.InnerException != null)
            {
                MessageBox.Show(e.Exception.InnerException.Message, "Error message", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
            }
            else
            {
                MessageBox.Show(e.Exception.Message, "Error message", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
            }
            e.Handled = true;
        }

        #endregion // Error handling
    }
}
